﻿using System.Text;
using MoXAML.Infrastructure;
using EnvDTE;
using EnvDTE80;
using System.Xml;
using System.Globalization;

namespace MoXAML.PowerToys.Classic
{
    /// <summary>
    /// This command sets the field modifier to private for XAML items marked with the x:Name attribute. See Josh Smith's
    /// blog entry at <see href="http://joshsmithonwpf.wordpress.com/2009/01/07/make-use-of-the-xfieldmodifier-attribute/"/>
    /// for more detail.
    /// </summary>
    /// <remarks>
    /// This command looks through all of the xaml files in the selected project for items marked with x:Name and
    /// without the x:FieldModifier attribute. When it finds them, it adds the x:FieldModifier attribute and sets it to private.
    /// </remarks>
    public class FieldModifierCommand : CommandBase
    {
        private const string XAML_NAMESPACE = "http://schemas.microsoft.com/winfx/2006/xaml";

        public FieldModifierCommand() : base()
        {
            CommandName = "CommandFieldModifier";
            Caption = "Add FieldModifier";
            ParentCommandBar.Add(CommandBarType.SolutionContextMenu);
        }

        /// <summary>
        /// Initialize a new instance of <see cref="FieldModifierCommand"/>
        /// </summary>
        public override void Execute()
        {
            base.Execute();

            Application.Documents.SaveAll();
            FindXamlFiles(Project.ProjectItems);
        }

        /// <summary>
        /// Recursively hunt through the project for xaml files.
        /// </summary>
        private void FindXamlFiles(ProjectItems items)
        {
            foreach (ProjectItem pi in items)
            {
                if (pi.Name.EndsWith("xaml", true, CultureInfo.CurrentUICulture))
                {
                    Parse(pi.get_FileNames(0));
                }
                else if (pi != null && pi.ProjectItems.Count > 0)
                {
                    FindXamlFiles(pi.ProjectItems);
                }
            }
        }


        /// <summary>
        /// Parse the xaml file for the relevant attributes.
        /// </summary>
        /// <param name="file"></param>
        private void Parse(string file)
        {
            XmlDocument doc = new XmlDocument();
            doc.PreserveWhitespace = true;
            doc.Load(file);

            XmlElement el = doc.DocumentElement;

            XmlNamespaceManager mgr = GetNamespace(doc);

            // Now, parse the file...
            string attribute = "x:FieldModifier";
            string value = "private";

            // Retrieve all nodes (the //*) where the attribute x:Name isn't empty, but where
            // x:FieldModifier is.
            string expression = "//*[string-length(normalize-space(@x:Name)) > 0 and string-length(normalize-space(@x:FieldModifier)) = 0]";

            XmlNodeList list = el.SelectNodes(expression, mgr);

            if (list != null && list.Count > 0)
            {
                foreach (XmlNode node in list)
                {
                    XmlAttribute attrib = doc.CreateAttribute(attribute, XAML_NAMESPACE);
                    attrib.Value = value;
                    node.Attributes.Append(attrib);
                }
                // Only save the file if we've added a node.
                doc.Save(file);
            }
        }

        /// <summary>
        /// Create the namespace manager, and add the presentation and xaml items to it.
        /// </summary>
        /// <param name="doc">The Xml document.</param>
        /// <returns>The populated namespace manager.</returns>
        private XmlNamespaceManager GetNamespace(XmlDocument document)
        {
            XmlNamespaceManager mgr = new XmlNamespaceManager(document.NameTable);
            mgr.AddNamespace("x", XAML_NAMESPACE);
            return mgr;
        }

    }
}